/*!  \file  Task.h
 *	  \brief  
 * 
 *   \details   details
 *   \author    Yordan Madzhunkov German Research School for Simulation Sciences GmbH
 *   \version   1.0
 *   \date      07/16/2013 03:22:36 PM
 *   \copyright GNU Public License.
 */
#pragma once

#include "TaskID.h"

class Task;
class TaskManager;
class Matrix;
class GKBisector;
class GK;
class BiMRRR;
struct Interval;

typedef bool (Task::*computeFunc)(TaskManager & manager) ;

typedef bool (Task::*childFinishedFunc)(TaskManager & manager, 
									const TaskID childID);
typedef void (Task::*finishFunc)(TaskManager & manager);

#define TASK_METHODS(x) \
	bool compute##x( TaskManager & manager);\
	bool child##x(TaskManager & manager, \
									const TaskID childID );\
	void finish##x(TaskManager & manager);

#include "macros.h"
class Task {
public:
	enum Type { 
					CountWithBisector          = 0,
					RefineIntervalWithBisector = 1,
					SplitIntervalWithBisector  = 2,
					IdentifyClusters           = 3,
					ShiftRepresentation        = 4,
					RefineSingletonWithGK      = 5,
					ComputeSingleton            = 6,
					ComputeCluster             = 7,
					BiMRRRTask                 = 8,
					End                        = 9
				 };
	

	void reset(Type type_,	void * dataPtr_, int localID_, TaskID parentID_);
	// precompute is called once per task
	// all of the intialsubtask should be created here and added to 
	// work queue
	//
	// returns:
	// true  -> if no subtasks were created
	// false -> otherwise
	bool precompute   (TaskManager & manager) {
		return (this->*computeF)(manager);
	}

	// returns:
	// true  -> if all subtasks are compleate
	// false -> otherwise
	// MUST BE thread safe 
	bool childFinished(TaskManager & manager, 
									const TaskID childID) {
		return (this->*childF)(manager, childID);
	}

	// this method will be called only once, by single thread
	// when all the subtask are compleate 
	// and after we call parent.childFinished
	void finish(TaskManager & manager) {(this->*finishF)(manager);	};


	TaskID getParentID() const { return parentID; };
	Type   getType()     const { return type; }
	friend class TaskManager;
	friend std::ostream& operator<<(std::ostream& os, const Task & t);
	void print(std::ostream& os, const int taskID);
	void setCount(TaskManager & man, const int val      ); // thread safe
	int  addCount(TaskManager & man, const int updateVal); // thread safe

private:

	TASK_METHODS(  CountWithBisector           );
	TASK_METHODS(  RefineIntervalWithBisector  );
	TASK_METHODS(  SplitIntervalWithBisector   );
	TASK_METHODS(  IdentifyClusters            );
	TASK_METHODS(  ShiftRepresentation         );
	TASK_METHODS(  RefineSingletonWithGK       );
	TASK_METHODS(  ComputeSingleton            );
	TASK_METHODS(  ComputeCluster              );
	TASK_METHODS(  BiMRRRTask                  );

	// when initial counts are computed, 
	int checkInterval(TaskManager& man,
							GKBisector* bis, 
							 Interval * all,
							const int leftCount,
							const int rightCount,
							const int step,
							const int nSteps,
							const TaskID parentID);

	//creates singeltons and cluster tasks and pushes them into ready Queue
	//returns the number of task created
	int createSingeltonAndClustersTasks(TaskManager & man, BiMRRR * bimrrr, int start, int stop) const;

	// count   == 0 , ready for exution of computaiton
	// count   > 0  , we still have to wait for same task finish
	// count   < 0  , some thread already is proccesing this task

	void * dataPtr;
	Type   type;
	int    localID; // this keeps importent info for the parent
	TaskID parentID;
	int 	 threadID; // local thread id,
	volatile	int       count;

	computeFunc      	  computeF;
	childFinishedFunc 	 childF; 
	finishFunc 				finishF;
};
/*  
struct BiMRRRTaskData {
// input data 
	BiMRRR     *   bimrrr; // in case we need some methods
	Matrix     *   B;    // pointer to original matrix
	Matrix     *   U;    // pointer to U matrix
	Matrix     *   S;    // pointer to S matrix
	Matrix     *   V;    // pointer to V matrix

// MRRR related data, that is needed for computation
	GK         * nat;    // Native, 0 shifted root representtion
	GKBisector * bis;	   // B^TB bisector pointer
	Interval   * all;    // Intervals 
};
*/
